package com.lagou.edu.factory;

import com.lagou.edu.utils.ClassUtils;
import com.lagou.edu.zhujie.Autowired;
import com.lagou.edu.zhujie.Service;
import com.lagou.edu.zhujie.Transactional;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author yusiji
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> objectMap = new HashMap<>();

    static {

        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//package");
            for (Element element : list) {
                String path = element.attributeValue("path");
                Set<Class<?>> classes = ClassUtils.getClasses(path);
                for (Class<?> classTemp : classes) {
                    Service service = classTemp.getAnnotation(Service.class);
                    if (service != null) {
                        if (StringUtils.isEmpty(service.value())) {
                            //由于getName获取的是全限定类名，所以要分割去掉前面包名部分
                            String[] names = classTemp.getName().split("\\.");
                            objectMap.put(names[names.length - 1], classTemp.newInstance());
                        } else {
                            objectMap.put(service.value(), classTemp.newInstance());
                        }
                    }
                }

                for (Map.Entry<String, Object> a : objectMap.entrySet()) {
                    Object o = a.getValue();
                    Class c = o.getClass();
                    Field[] fields = c.getDeclaredFields();
                    Method[] methods = c.getMethods();
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(Autowired.class)
                                && field.getAnnotation(Autowired.class).required()) {
                            String[] names = field.getType().getName().split("\\.");
                            String name = names[names.length - 1];

                            for (int j = 0; j < methods.length; j++) {
                                Method method = methods[j];
                                if (method.getName().equalsIgnoreCase("set" + name)) {
                                    method.invoke(o, objectMap.get(name));
                                }
                            }
                        }

                    }

                    for (Method method: methods) {
                        if(c.isAnnotationPresent(Transactional.class) || method.isAnnotationPresent(Transactional.class)){
                            //获取代理工厂
                            ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                            Class[] face = c.getInterfaces();//获取类c实现的所有接口
                            //判断对象是否实现接口
                            if(face!=null&&face.length>0){
                                //实现使用JDK
                                o = proxyFactory.getJdkProxy(o);
                            }else{
                                //没实现使用CGLIB
                                o = proxyFactory.getCglibProxy(o);
                            }
                        }
                    }
                    objectMap.put(a.getKey(), o);
                }


            }
        } catch (DocumentException | ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static Object getBean(String id) {
        return objectMap.get(id);
    }


}
